<template>
    <canvas ref="container" id="renderCanvas" style="width: 100vw;height: 100vh;cursor: none"/>
    <div v-if="gameOver" class="container">
        <div class="dialog">
            <p class="once-again">本轮分数：{{score}}</p>
            <p class="once-again">再来一次</p>
            <p class="once-again">再来一次~~</p>
            <button type="button" class="btn btn-success" @click="init">开始</button>
            &emsp;
            <button type="button" class="btn btn-danger" @click="back_home">退出</button>
        </div>
    </div>
</template>

<script>
    //import * as BABYLON from '@babylonjs/core/Legacy/legacy';
    import  * as BABYLON from 'babylonjs';
    import * as earcut from 'earcut'
    import * as BABYLONMA from 'babylonjs-materials/babylon.waterMaterial';
    import * as ammo from 'ammo.js'
    const axios=require("axios");
    export default {
        name: "test",
        data() {
            return {
                username: '',
                canvas:'',
                gameOver:false,
                player:{   //游戏人物位置，速度，跑道，状态信息
                    mesh:null,
                    position: {
                        x: null,
                        y: null,
                        z: null,
                    },
                    speed:1,
                    runway:1,
                    status:"run",
                },
                ground:'',
                ZSpeed:'',
                key:[],
                input:{},
                hinder:{  //障碍的状态，位置
                    status:null,
                    x:null,
                    y:null,
                    z:null,
                },
                hinders:[],
                hinder_con:null,
                prize_cons:[],  //存储奖品的位置，状态信息数组
                prizemanager:[], //奖品的资源管理器数组，主要存储场景奖品资源管理器
                shark: {  //鲨鱼速度，位置信息。
                    mesh:null,
                    position: {
                        x:null,
                        y:null,
                        z:null,
                    },
                    speed:0.6,
                },
                warn:false,
                hinder_timer:null,
                keys:null,
                keysUp : [87],
                keysDown : [83],
                keysLeft : [65],
                keysRight : [69],
                keysShift : [16],
                onKeyUp:null,
                onKeyDown:null,
                score:0,
                publicPath:process.env.BASE_URL   //public目录下
            }
        },
        mounted(){
            let that=this
            const canvas=this.$refs.container;
            let engine;
            engine=new BABYLON.Engine(canvas,true);
            //const scene=that.createScene();
            const scene=new BABYLON.Scene(engine);
            that.hinder_con=new BABYLON.AssetContainer(scene);  //初始化障碍资源管理器。
            //添加弧形相机，并且禁止放大缩小。当lowerRadiusLimit=upperRadiusLimit，禁止放大缩小。
            const camera=new BABYLON.ArcRotateCamera('camera', 3*Math.PI/2, Math.PI / 4, 100, new BABYLON.Vector3(0, -5, 0), scene);
            camera.attachControl(this.canvas,true);
            camera.speed=0.02;
            //camera.angle=Math.PI*2
            //camera.direction = new BABYLON.Vector3(Math.cos(Math.PI*2), 0, Math.sin(camera.angle));
            scene.activeCamera=camera
            scene.activeCamera.attachControl(canvas,true);
            camera.lowerRadiusLimit=10;
            camera.upperRadiusLimit = 10;
            camera.wheelDeltaPercentage = 0.01;

            //添加灯光
            const light=new BABYLON.HemisphericLight('light1',new BABYLON.Vector3(1,1,0),scene);
            //添加一个天空盒，形成游戏场景
            const skybox=BABYLON.MeshBuilder.CreateBox("skybox",{size:5000,width:5000,height:10000},scene);
            const skyboxMaterial=new BABYLON.StandardMaterial("skyBox",scene);
            skyboxMaterial.backFaceCulling = false;
            skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("https://playground.babylonjs.com/textures/TropicalSunnyDay", scene);
            skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
            skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
            skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
            skyboxMaterial.disableLighting = true;
            skybox.material = skyboxMaterial;

            //添加地面
            this.ground = BABYLON.MeshBuilder.CreateGround("ground", {width:120, height:5000,subdivisions:16});
            this.ground.position.y=-7;
            const water=new BABYLONMA.WaterMaterial("water",scene);
            water.bumpTexture=new BABYLON.Texture(require("../assets/3dplanewar/Sea/9313/Water/water2.png"),scene);
            // water.windForce = 5;
            water.waveHeight = 1;  // 海浪的高度:但是海浪高了之后就显示出三角形了，过渡不是很流畅
            water.bumpHeight = 0;   // 表示反射和折射的扰动。波光粼粼的感觉.越小就越是原本添加的天空的贴图样式
            water.waveLength = 0.1; //波长
            water.windDirection = new BABYLON.Vector2(1, 1);  //风向
            water.waterColor = new BABYLON.Color3(0, 0, 221 / 255);
            water.colorBlendFactor = 0.0;
            water.waterColor = new BABYLON.Color3(0, 0, 221 / 255);
            water.colorBlendFactor = 0.0;
            water.addToRenderList(skybox);
            //groundMat.diffuseTexture=new BABYLON.Texture(require("../assets/3dplanewar/Sea/9313/Water/water3.png"));
            this.ground.material=water;
            //this.ground.physicsImpostor = new BABYLON.PhysicsImpostor(this.ground, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 0, restitution: 0.5 }, scene);
            engine.runRenderLoop(function () {
                scene.render();
            })
            canvas.addEventListener('resize',function () {
                engine.resize();
                camera.setTarget(that.player._postActivate())
            })
            this.drawAll(camera,scene);  //画出游戏人物，以及道路两边的树

            //获取用户键盘输入，注册键盘监听事件。
            this.inputMap = {};
            scene.actionManager = new BABYLON.ActionManager(scene);
            scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyDownTrigger, function (evt) {
                that.inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
            }));
            scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyUpTrigger, function (evt) {
                that.inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
            }));

            //自调用一个定时器函数，60帧循环监听游戏人物状态
            (function animloop() {
                that.PlayerGet();
                that.PlayerHit();
                window.requestAnimationFrame(animloop);
            })();
            //that.PlayerHit(scene)
            //that.movePlayer(scene);
            that.init(scene);  //开始函数，运行后添加障碍，奖品。
        },
        methods:{
            init(scene){
                let that=this;
                if(that.gameOver){
                    that.prizemanager=[];
                    that.prize_cons=[];
                    that.hinders=[];
                    that.hinder_con.removeAllFromScene();
                    that.player.speed=1;
                    that.send_score();
                    that.score=0;
                }
                this.drawHinder();
                this.drawPrize(scene);
                that.warn=false;
                that.gameOver=false;
            },

            drawAll(camera,scene){
                let that=this;
                this.drawPlayer(camera,scene);
                this.drawShark(scene);
                this.drawTree(scene);
            },

            drawPlayer(camera,scene){
                let that=this;
                let foot=BABYLON.SceneLoader.ImportMeshAsync("","/3d_run/","Surf_board.obj",scene); //加载冲浪板模型，获取的是一个Promise对象
                BABYLON.SceneLoader.ImportMeshAsync("", "https://assets.babylonjs.com/meshes/", "HVGirl.glb",scene).then(res =>{
                    that.player.mesh=res.meshes[0];
                    that.player.mesh.scaling.scaleInPlace(0.1);
                    that.player.mesh.rotation.x=Math.PI/2;
                    foot.then(foot_res=>{     //将冲浪板模型旋转，parent固定在人物脚上，scaling调整模型缩放。
                        let board=foot_res.meshes[0];
                        board.rotation.x=Math.PI/2;
                        board.parent=that.player.mesh;
                        board.position.z=-25;
                        board.scaling.scaleInPlace(0.5);
                        //给模型贴图
                        const s_board=new BABYLON.StandardMaterial("boadr_ma",scene);
                        s_board.diffuseTexture=new BABYLON.Texture(require("../assets/3dplanewar/Arch32_050_diffuse.jpg"));
                        board.material=s_board
                    })
                    that.hinder_con.meshes.push(that.player.mesh)
                    camera.target=that.player.mesh;  //相机镜头跟踪游戏模型
                    let amating=true;
                    const walkAnim = scene.getAnimationGroupByName("Walking");
                    scene.onBeforeRenderObservable.add(()=>{
                        //游戏结束时，将人物模型位置置0，并返回。
                        if(that.gameOver){
                            that.player.mesh.position.z=0;
                            return;
                        }
                        if(that.player.mesh.position.z>=1900){ //当人物位置到“终点”时，清楚障碍，奖品，并重置warn，人物速度+0.1；
                            that.player.mesh.position.z=0
                            that.prizemanager=[];
                            that.prize_cons=[];
                            that.hinder_con.removeAllFromScene();
                            that.drawHinder();
                            that.drawPrize(scene);
                            that.player.speed+=0.1;
                            that.score+=2;
                            that.warn=false;
                        }
                        that.player.mesh.position.z+=that.player.speed;

                        that.player.mesh.position.x = this.set_runway(that.player.runway);
                        //人物监听，对用户的按键予以相应处理
                        let keydown=false;    //player 左-60，中0,右60。
                        if (that.inputMap["a"]) {
                            //that.player.rotate(BABYLON.Vector3.Up(), -heroRotationSpeed);
                            if(that.player.runway!=0) {
                                walkAnim.start(false, 0.5, walkAnim.from, walkAnim.to / 2, false);
                                //that.player.runway--;
                                keydown = true;
                            }
                        }
                        if (that.inputMap["d"]) {
                            if(that.player.runway!=2) {
                                walkAnim.start(false, 0.5, walkAnim.from, walkAnim.to / 2, false);
                                //that.player.runway++;
                                keydown = true;
                            }
                        }
                        if(that.inputMap["s"]){
                            keydown=true
                        }
                        if(that.inputMap[" "]){
                            keydown=true
                        }
                        if(keydown){
                            if(!amating){
                                amating=true;
                                if(that.inputMap["s"]){
                                    that.player.status="sit"
                                    that.player.mesh.position.y=-5
                                }
                                if(that.inputMap[" "]){
                                    that.player.mesh.position.y=10
                                    that.player.status="jump"
                                }if(that.inputMap["a"]){
                                    that.player.runway--;
                                }if(that.inputMap["d"]){
                                    that.player.runway++;
                                }
                            }
                        }else {
                            if(amating){
                                that.player.status="run"
                                that.player.mesh.position.y=0
                                amating=false
                            }
                        }
                        //实时将模型位置存储在player对象中。以便其他函数使用。
                        that.player.position.x=that.player.mesh.position.x;
                        that.player.position.y=that.player.mesh.position.y;
                        that.player.position.z=that.player.mesh.position.z;
                    })
                })
            },

            //传入跑道，返回人物X坐标位置
            set_runway(runway){
                let that=this;
                //console.log(that.player.mesh)
                switch (runway) {
                    case 0:
                        return -30;
                    case 1:
                        return 0;
                    case 2:
                        return 30;
                }
            },

            //道路两旁画树
            drawTree(scene){
                const spriteManager=new BABYLON.SpriteManager("treemanager","https://playground.babylonjs.com/textures/palm.png",2000,{width:512,height:1024},scene);
                for (let i=0;i<2500;i++){
                    if(i%20==0) {
                        const tree = new BABYLON.Sprite("tree", spriteManager);
                        tree.position.x = -58;
                        tree.position.y=10;
                        tree.position.z = i;
                        tree.height = 40;
                        tree.width=10
                    }
                }
                for (let i=0;i<2500;i++){
                    if(i%20==0) {
                        const tree = new BABYLON.Sprite("tree", spriteManager);
                        tree.position.x = 58;
                        tree.position.y=10;
                        tree.position.z = i;
                        tree.height = 40;
                        tree.width=10
                    }
                }
            },

            //设置障碍
            drawHinder()
            {
                let that=this;
                for(let i=0;i<15;i++) {
                    //随机获取障碍跑道0-2，传入后得到X坐标。
                    let hinder_way=Math.floor(Math.random()*3);
                    let Xposition = that.set_runway(hinder_way);
                    let hinder_msg=that.setGoods_msg("hinder"); //获取障碍高度，宽，长度。

                    //创建一个盒子，作为障碍，可替换。
                    let run_hinder = BABYLON.MeshBuilder.CreateBox("box", {height: hinder_msg.height, width: 30, depth: 10});
                    run_hinder.position = new BABYLON.Vector3(Xposition, hinder_msg.y, Math.floor(Math.random() * 3000)+100)
                    let hinder={};
                    //障碍坐标，状态存入一个障碍数组中，用于其他函数使用。
                    hinder.x=Xposition;
                    hinder.y=hinder_msg.y;
                    hinder.z=run_hinder.position.z;
                    hinder.status=hinder_msg.status;
                    that.hinders.push(hinder)
                    that.hinder_con.meshes.push(run_hinder); //将障碍模型存放在障碍资源管理器中。
                }
            },

            //设置物品信息
            setGoods_msg(Goods){
                let num=Math.floor(Math.random()*3);
                if(Goods=="hinder") {
                    switch (num) {
                        case 0:
                            return {y: 16.5, status: "sit", height: 30}
                        case 1:
                            return {y: 10, status: "kill", height: 30}
                        case 2:
                            return {y: 0, status: "jump", height: 10};
                    }
                }else {
                    let num_prize=Math.floor(Math.random()*2)
                    switch (num_prize) {
                        case 0:
                            return {y: 10, status: "run", height: 30}
                        case 1:
                            return {y: 2, status: "jump", height: 30};
                    }
                }
            },

            //添加鲨鱼模型
            drawShark(scene){
                let that=this;
                BABYLON.SceneLoader.ImportMeshAsync("","/3d_run/","shark.obj",scene).then(res=>{
                    that.shark.mesh=res.meshes[0];
                    that.shark.mesh.scaling.scaleInPlace(0.02);
                    that.shark.mesh.rotation.y=Math.PI
                    that.shark.mesh.position=new BABYLON.Vector3(that.player.position.x,that.player.position.y,that.player.position.z-15);
                    scene.onAfterRenderObservable.add(()=>{
                        //当人物跑完一轮后，重置Z坐标
                        if(that.player.position.z>=1900){
                            that.shark.mesh.position.z=-13;
                        }
                        if(that.gameOver){
                            that.shark.mesh.position.z=-13;
                            return;
                        }
                        //当人物碰到撞到障碍物后，立即跟踪人物z坐标后的-13.
                        if(that.warn){
                            that.shark.mesh.position.z=that.player.position.z-13;
                        }
                        that.shark.mesh.position.x=that.player.position.x; //实时跟踪用户所在X坐标
                        that.shark.mesh.position.y=that.player.position.y-5;
                        that.shark.mesh.position.z+=0.8;
                        that.shark.position.z=that.shark.mesh.position.z;
                        that.shark.position.x=that.shark.mesh.position.x;
                        that.shark.position.y=that.shark.mesh.position.y;
                    })
                })
            },

            //画奖品。
            drawPrize(scene){
                let that=this;
                for(let i=0;i<20;i++) {
                    //向设置障碍物那样设置奖品的坐标，信息。并为每一个奖品建立一个资源管理器。以便逐个删除
                    let prize_way=Math.floor(Math.random()*3);
                    let prize_man=new BABYLON.AssetContainer();
                    let Xposition = that.set_runway(prize_way);
                    let prize_msg=that.setGoods_msg("prize");
                    let prize_box = BABYLON.MeshBuilder.CreateBox("box", {height: 2, width: 2, depth: 2});
                    prize_box.position = new BABYLON.Vector3(Xposition, prize_msg.y, Math.floor(Math.random() * 5000))
                    let prize={};
                    prize.x=Xposition;
                    prize.y=prize_box.position.y;
                    prize.z=prize_box.position.z;
                    prize.status=prize_msg.status;
                    //将奖品对象坐标，状态存放在一个奖品数组容器内。
                    that.prize_cons.push(prize)
                    //将奖品模型放入奖品管理器中
                    prize_man.meshes.push(prize_box);
                    //将奖品管理器放入奖品管理器数组中。
                    that.prizemanager.push(prize_man);
                }
            },

            //人物模型碰撞障碍。
            PlayerHit(){
                let that=this;
                that.hinders.forEach(item => {
                    if (that.player.position.z>=item.z-0.5 && that.player.position.z<=item.z
                        && that.player.position.x==item.x
                        && that.player.status!=item.status) {
                        //碰撞到则”召唤“鲨鱼。若鲨鱼已被”召唤“,游戏结束。
                        if(that.warn){
                            that.gameOver=true;
                        }else {
                            that.warn=true;
                        }
                    }
                });

            },

            //人物碰撞奖品
            PlayerGet(){
                let that=this;
                that.prize_cons.forEach(function (item,index,arr) {
                    if (that.player.position.z>=item.z-0.5 && that.player.position.z<=item.z
                        && that.player.position.x==item.x
                        && that.player.status!=item.status) {
                        //碰撞到则分数加1，并且根据索引index,找到奖品资源管理器数组中的资源管理器，并删除容器内的所有东西。
                        //实现碰撞到则删除
                        that.score++;
                        that.prizemanager[index].removeAllFromScene();
                        //that.prizemanager.splice(index,1);
                        //console.log(that.prizemanager.length)
                    }
                })
            },

            //向服务器发送分数
            send_score() {
                let data=new URLSearchParams();
                data.append("username",this.$cookies.get("username"));
                data.append("Parkour",this.score);
                axios.post("/user/updateParkour",data).then(res=>{
                    console.log(res);
                })
            },

            //发送分数，退出游戏
            back_home(){
                this.send_score();
                this.$router.push("/home");
            }

        }
    }
</script>

<style lang="scss">
    .container {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        width: 900px;
        height: 600px;
        left: 0;
        background-color: rgba(0, 0, 0, 0.3);
        text-align: center;
        font-size: 0;
        white-space: nowrap;
        overflow: auto;
    }
    .container:after {
        content: "";
        display: inline-block;
        height: 100%;
        vertical-align: middle;
    }
    .dialog {
        width: 400px;
        height: 300px;
        background: rgba(255, 255, 255, 0.5);
        box-shadow: 3px 3px 6px 3px rgba(0, 0, 0, 0.3);
        display: inline-block;
        vertical-align: middle;
        text-align: left;
        font-size: 28px;
        color: #fff;
        font-weight: 600;
        border-radius: 10px;
        white-space: normal;
        text-align: center;
        .once-again-btn {
            background: #1f9a9a;
            border: none;
            color: #fff;
        }
    }
</style>